Skip to content

[clang][SYCL] Add sycl_external attribute and restrict emitting device code #140282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 39 commits into
base: main
Choose a base branch
from

Conversation

schittir
Copy link
Contributor

@schittir schittir commented May 16, 2025

This patch is part of the upstreaming effort for supporting SYCL language front end.
It makes the following changes:

  1. Adds sycl_external attribute for functions
  2. Adds checks to avoid emitting device code when sycl_external and sycl_kernel_entry_point attributes are not enabled
  3. Fixes test failures caused by the above changes

Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this @schittir! I completed an initial pass of all of the code, but still need to look more closely at the documentation updates.

@schittir
Copy link
Contributor Author

Thanks for working on this @schittir! I completed an initial pass of all of the code, but still need to look more closely at the documentation updates.

Thank you for the initial pass review, Tom!

Copy link

github-actions bot commented May 26, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@schittir schittir changed the title Add sycl_external attribute [SYCL] Add sycl_external attribute Jun 10, 2025
@schittir schittir marked this pull request as ready for review June 10, 2025 16:16
@schittir schittir changed the title [SYCL] Add sycl_external attribute [clang][SYCL Upstreaming] Add sycl_external attribute Jun 10, 2025
@Fznamznon Fznamznon added the SYCL https://registry.khronos.org/SYCL label Jun 10, 2025
@schittir schittir changed the title [clang][SYCL Upstreaming] Add sycl_external attribute [clang][SYCL Upstreaming] Add sycl_external attribute and restrict emitting code Jun 10, 2025
@schittir schittir changed the title [clang][SYCL Upstreaming] Add sycl_external attribute and restrict emitting code [clang][SYCL Upstreaming] Add sycl_external attribute and restrict emitting device code Jun 10, 2025
schittir and others added 3 commits June 10, 2025 14:48
@Fznamznon Fznamznon changed the title [clang][SYCL Upstreaming] Add sycl_external attribute and restrict emitting device code [clang][SYCL] Add sycl_external attribute and restrict emitting device code Jun 11, 2025
Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @schittir, here are my initial review comments. I expect to review the newly added tests more closely once these comments are all addressed.

Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional comments for diagnostics and tests.

@Fznamznon
Copy link
Contributor

Oops new tests are failing.

Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking good @schittir! I added some comments for minor issues. I still need to review the tests more closely.

Comment on lines 4087 to 4090
// SYCL spec 2020
// The first declaration of a function with external linkage must
// specify sycl_external attribute.
// Subsequent declarations may optionally specify this attribute.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is helpful to quote the specification verbatim so that the intent is understood from the quoted context in the event that the specification changes.

Suggested change
// SYCL spec 2020
// The first declaration of a function with external linkage must
// specify sycl_external attribute.
// Subsequent declarations may optionally specify this attribute.
// SYCL 2020 section 5.10.1, "SYCL functions and member functions linkage":
// When a function is declared with SYCL_EXTERNAL, that macro must be
// used on the first declaration of that function in the translation unit.
// Redeclarations of the function in the same translation unit may
// optionally use SYCL_EXTERNAL, but this is not required.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the suggestion.
I will implement the wording as it is except for one point - wouldn't it be better to use sycl_external attribute instead of SYCL_EXTERNAL macro which is not yet implemented?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend towards quoting the text verbatim. Readers are likely to be aware that the intent of the sycl_external attribute is to implement the SYCL_EXTERNAL macro; that was made clear in the documentation for the attribute.

Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more comments. I still haven't finished my review of sycl-external-attr.cpp, but will try to later today or over the weekend.

Comment on lines +1 to +19
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s

// expected-error@+1{{'clang::sycl_external' attribute only applies to functions}}
[[clang::sycl_external]] int a;


// expected-error@+2{{'clang::sycl_external' attribute only applies to functions}}
struct s {
[[clang::sycl_external]] int b;
};

// expected-error@+1{{'clang::sycl_external' attribute takes no arguments}}
[[clang::sycl_external(3)]] void bar() {}

// FIXME: The first declaration of a function is required to have the attribute.
// The attribute may be optionally present on subsequent declarations
int foo(int c);

[[clang::sycl_external]] void foo();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These examples are all grammatically correct because the tokens form a valid attribute-specifier and are placed in a grammatically correct position in each declaration. The only part of the syntax that would be grammatically specific to sycl_external would be the syntax of its arguments; if it took any. I think it is reasonable to keep (invalid) examples like bar() that exercise arguments here since, if the attribute were extended to accept an argument, this would be the right place to validate them. I think these are good examples to test in this file:

[[clang::sycl_external()]] void bad1();
[[clang::sycl_external(,)]] void bad2();
[[clang::sycl_external(3)]] void bad3();
[[clang::sycl_external(4,)]] void bad4();

The a and b examples exercise appertainment and I think would best be placed in a sycl-external-attr-appertainment.cpp test.

The foo() example is redundant; similar declarations are validated by sycl-external-attr.cpp.

Comment on lines +12885 to +12889
def err_sycl_attribute_avoid_main : Error<
"'sycl_external' cannot be applied to the 'main' function">;
def err_sycl_attribute_avoid_deleted_function
: Error<"'sycl_external' cannot be applied to an explicitly deleted "
"function">;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using "invalid" in place of "avoid" for consistency with other nearby diagnostics. Please keep the wrapping and indentation style consistent with nearby code.

Suggested change
def err_sycl_attribute_avoid_main : Error<
"'sycl_external' cannot be applied to the 'main' function">;
def err_sycl_attribute_avoid_deleted_function
: Error<"'sycl_external' cannot be applied to an explicitly deleted "
"function">;
def err_sycl_attribute_invalid_main : Error<
"'sycl_external' cannot be applied to the 'main' function">;
def err_sycl_attribute_invalid_deleted_function : Error<
"'sycl_external' cannot be applied to an explicitly deleted function">;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wrapping and indentation style was modified by git-clang-format tool, which surprised me! I'll revert it.

@@ -0,0 +1,12 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s

// expected-warning@+1{{'clang::sycl_external' attribute ignored}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// expected-warning@+1{{'clang::sycl_external' attribute ignored}}
// These tests validate that the sycl_external attribute is ignored when SYCL
// support is not enabled.
// expected-warning@+1{{'clang::sycl_external' attribute ignored}}

Comment on lines +9 to +12
// expected-warning@+2{{'clang::sycl_external' attribute ignored}}
namespace not_sycl {
[[clang::sycl_external]] void foo() {}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the namespace scoped example adds anything here, but I think it would be good to exercise a function template.

Suggested change
// expected-warning@+2{{'clang::sycl_external' attribute ignored}}
namespace not_sycl {
[[clang::sycl_external]] void foo() {}
}
// expected-warning@+2{{'clang::sycl_external' attribute ignored}}
template<typename>
[[clang::sycl_external]] void ft(T) {}
template void ft(int);

@@ -0,0 +1,70 @@
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s
// RUN: %clang_cc1 -fsycl-is-device -std=c++20 -fsyntax-only -verify -DCPP20 %s
// Semantic tests for sycl_external attribute
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add spacing to avoid potential merge conflicts or careless additions of additional RUN lines in the future.

Suggested change
// Semantic tests for sycl_external attribute
// Semantic tests for the sycl_external attribute.

Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I finished an initial pass through all of the tests. I'll do another round once you've had a chance to catch up on the comments so far.

Comment on lines +1 to +2
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s
// RUN: %clang_cc1 -fsycl-is-device -std=c++20 -fsyntax-only -verify -DCPP20 %s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency and in case the default changes some day.

Suggested change
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s
// RUN: %clang_cc1 -fsycl-is-device -std=c++20 -fsyntax-only -verify -DCPP20 %s
// RUN: %clang_cc1 -fsycl-is-device -std=c++17 -fsyntax-only -verify -DCPP17 %s
// RUN: %clang_cc1 -fsycl-is-device -std=c++20 -fsyntax-only -verify -DCPP20 %s

Comment on lines +15 to +16
[[clang::sycl_external]] // expected-error {{'sycl_external' can only be applied to functions with external linkage}}
void func4(UnnX) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good test. I suggest trying to keep the tests self-contained as much as possible so that they are easy to review without having to look elsewhere. A consistent naming scheme can help to avoid unintended reuse of symbols that can potentially affect test outcomes.

Suggested change
[[clang::sycl_external]] // expected-error {{'sycl_external' can only be applied to functions with external linkage}}
void func4(UnnX) {}
// expected-error@+2 {{'sycl_external' can only be applied to functions with external linkage}}
namespace { struct S4 {}; }
[[clang::sycl_external]] void func4(S4) {}


[[clang::sycl_external]] // expected-error {{'sycl_external' can only be applied to functions with external linkage}}
void func4(UnnX) {}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other interesting internal linkage cases:

The attribute from the primary template is inherited by the specialization instantiated by the explicit template instantiation declaration. It could be argued that, because the explicit instantiation declaration names the symbol that causes the instantiated specialization to have internal linkage, a diagnostic should be produced here. However, other template shenanigans can produce similar results and would be difficult to diagnose well. I think we should ensure that a diagnostic is not issued for this case.

namespace { struct S6 {}; }
template<typename>
[[clang::sycl_external]] void func6() {}
template void func6<S6>();

In this case, the attribute from the primary template is not inherited by the specialization produced by the explicit specialization declaration. No diagnostic should be issued for this case. However, Clang doesn't implement the attribute inheritance semantics correctly (according to the standard), so if a diagnostic is issued, we should just add a FIXME comment and not worry about it.

namespace { struct S7 {}; }
template<typename>
[[clang::sycl_external]] void func7();
template<> void func7<S7>() {}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is another case. DPC++ does diagnose this one. https://godbolt.org/z/9E9aPn6eq

namespace { struct S8 {}; }
template<typename>
void func8();
template<> [[clang::sycl_external]] void func8<S8>() {}

Comment on lines +18 to +21
// The first declaration of a SYCL external function is required to have this attribute.
int foo(); // expected-note {{previous declaration is here}}

[[clang::sycl_external]] int foo(); // expected-error {{'clang::sycl_external' attribute does not appear on the first declaration}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid blank lines between declarations that are part of the same test so that it is easy to see that they go together. Use line markers and references in expected diagnostics to avoid line length issues.

Suggested change
// The first declaration of a SYCL external function is required to have this attribute.
int foo(); // expected-note {{previous declaration is here}}
[[clang::sycl_external]] int foo(); // expected-error {{'clang::sycl_external' attribute does not appear on the first declaration}}
// The first declaration of a SYCL external function is required to have this attribute.
// expected-error@+3 {{'clang::sycl_external' attribute does not appear on the first declaration}}
// expected-note@#func8-decl {{previous declaration is here}}
int func8(); // #func8-decl
[[clang::sycl_external]] int func8();

Comment on lines +39 to +41
class D {
[[clang::sycl_external]] void del() = delete; // expected-error {{'sycl_external' cannot be applied to an explicitly deleted function}}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test doesn't require a member function, but the presence of class D suggests that there is something specific to member functions here.

Suggested change
class D {
[[clang::sycl_external]] void del() = delete; // expected-error {{'sycl_external' cannot be applied to an explicitly deleted function}}
};
// expected-error {{'sycl_external' cannot be applied to an explicitly deleted function}}
[[clang::sycl_external]] void del() = delete;

[[clang::sycl_external]]
A() {}

[[clang::sycl_external]] void func3() {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validate a static member function too.

Suggested change
[[clang::sycl_external]] void func3() {}
[[clang::sycl_external]] void mf();
[[clang::sycl_external]] static void smf();

Comment on lines +62 to +64
[[clang::sycl_external]] int *func0() { return nullptr; }

[[clang::sycl_external]] void func2(int *) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume these are intended to exercise the generic address space limitations. Let's add a comment to make that clear. I don't know if any of the target devices we expect to support lack support for the generic address space, so we might not be able to effectively test a positive diagnostic, but it is good to ensure no diagnostic for the common case.

Suggested change
[[clang::sycl_external]] int *func0() { return nullptr; }
[[clang::sycl_external]] void func2(int *) {}
// Devices that do not support the generic address space shall not specify
// a raw pointer or reference type as the return type or as a parameter type.
[[clang::sycl_external]] int *func0();
[[clang::sycl_external]] int &func1();
[[clang::sycl_external]] int &&func2();
[[clang::sycl_external]] void func3(int *);
[[clang::sycl_external]] void func4(int &);
[[clang::sycl_external]] void func5(int &&);
template<typename T>
[[clang::sycl_external]] void func6(T) {}
template void func6(int *);
template<> [[clang::sycl_external]] void func6<int*>(int *) {}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
SYCL https://registry.khronos.org/SYCL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants